package niveles;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;

import niveles.creadores.CreadorDeParedes;
import niveles.creadores.CreadorDeTanquesEnemigos;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import principal.Constantes;
import principal.GameOver;
import principal.ObservadorPuntos;
import principal.Victoria;

import basicas.Posicion;

import ar.uba.fi.algo3.titiritero.*;
import avanzadas.Grabador;
 

import juego.AlgoTank;
import juego.Base;
import juego.ObjetoDelJuego;
import juego.Pared;
import juego.Tanque;
import juego.enemigos.*;
import juego.enums.tipoMovimiento;
import juego.vistas.IVista;
import juego.vistas.VistaGameOver;
import juego.vistas.VistaVictoria;

public class Escenario implements ObjetoVivo, Posicionable {
	private ArrayList<ObjetoDelJuego> objetosDelJuego;
	private ArrayList<ObjetoDelJuego> objetosDelJuegoEnEspera;
	private ArrayList<ObjetoDelJuego> objetosDelJuegoBorrar;
	
	private ArrayList<IVista> vistasDelJuego;
	
	private int puntos;
	private Tablero tablero;
	private ControladorJuego controlador;	
	private AlgoTank algoTank;
	private Base base;
	private Grabador grabador;
	private Posicion posicion;
	private int numeroNivel;
	private int cantidadOleada;
	CreadorDeTanquesEnemigos fabricador;
	private ObservadorPuntos observadorPuntos;
	private boolean cargando;
	public Escenario() {
		
		objetosDelJuego = new ArrayList<ObjetoDelJuego>();
		objetosDelJuegoEnEspera = new ArrayList<ObjetoDelJuego>();
		objetosDelJuegoBorrar = new ArrayList<ObjetoDelJuego>();
		vistasDelJuego = new ArrayList<IVista>();
		tablero = new Tablero();
		grabador = new Grabador(this);		
		posicion = new Posicion(0,0);
		fabricador = new CreadorDeTanquesEnemigos();
		cargando = false;
		
	}
	

	
	public void armarCon(ArrayList<Pared> paredes) {

		// Creo el tanque principal.		
		AlgoTank algoTank = new AlgoTank(new Posicion(7, 11));
		
		agregarObjetoVivo(algoTank);
		this.agregarParedes(paredes);

		// Creo la base.
		Base base = new Base(new Posicion(8, 11));
		agregarObjetoVivo(base);
	}
	
	private  void agregarTanquesEnemigos(ArrayList<TanqueEnemigo> tanquesEnemigos) {		 
		Iterator<TanqueEnemigo> itTanquesEnemigos = tanquesEnemigos.iterator();		
		while (itTanquesEnemigos.hasNext()) {
			TanqueEnemigo tanqueEnemigoActual = itTanquesEnemigos.next();						

			//resetearPosicion(tanqueEnemigoActual);			
			
			agregarObjetoVivo(tanqueEnemigoActual);
		}
	}
	
	private void agregarParedes(ArrayList<Pared> paredes){
		Iterator<Pared> itParedes = paredes.iterator();		
		while(itParedes.hasNext()){
			Pared paredActual=itParedes.next();
			agregarObjetoVivo(paredActual);
		}
	}
	
	public ControladorJuego getControlador() {
		return controlador;
	}
	
	public void setControlador(ControladorJuego controlador) {
		this.controlador = controlador;		
	}

	public Tablero getTablero() {
		return tablero;
	}
	
	public AlgoTank getAlgoTank() {
		return algoTank;
	}
	
	public ArrayList<ObjetoDelJuego> getObjetos() {
		return objetosDelJuego;
	}
	
	public void vivir() {	
		ArrayList<ObjetoDelJuego> lista;
		lista = objetosDelJuegoEnEspera;
		objetosDelJuegoEnEspera = new ArrayList<ObjetoDelJuego>();
				
		
		Iterator<ObjetoDelJuego> iterador = lista.iterator();
		ObjetoDelJuego o;
		while(iterador.hasNext()) {
			o = iterador.next();
			objetosDelJuego.add(o);
		}

		iterador = objetosDelJuegoBorrar.iterator();		
		while(iterador.hasNext()) {
			o = iterador.next();
			objetosDelJuego.remove(o);
		}
		objetosDelJuegoBorrar.clear();
				
		tablero.limpiar();		
		Iterator<ObjetoDelJuego> it = objetosDelJuego.iterator();
		while (it.hasNext()) {
			o = (ObjetoDelJuego)it.next();
			o.informarPosicion();
		}
		
		if (cargando == false) {
			if (contarTanquesEnemigos() == 0) {
				ArrayList<TanqueEnemigo> tanquesEnemigos = fabricador.crearTanques(cantidadOleada); 
				agregarTanquesEnemigos(tanquesEnemigos);
			}
		}
						
		if (puntos > (Constantes.PUNTOS_POR_NIVEL * this.numeroNivel)) {
			System.out.println(this.numeroNivel);
			this.iniciarNivel(this.numeroNivel + 1);}
		
		observadorPuntos.seActualizaronLosPuntos(puntos);
		observadorPuntos.seActualizoVidaAlgoTank(algoTank.getResistencia());
		observadorPuntos.seActualizoVidaBase(base.getResistencia());
	}
	
	private int contarTanquesEnemigos() {
		int cuenta = 0;
		
		Iterator<ObjetoDelJuego> iterador;
		iterador = objetosDelJuego.iterator();
		ObjetoDelJuego o;
		while (iterador.hasNext() && cuenta == 0) {
			o = iterador.next();
			if (o instanceof TanqueEnemigo) {
				cuenta = cuenta + 1;				
			}
		}		
		return cuenta;
	}

	public IVista generarVistaParaObjeto(ObjetoDelJuego o) {
		String s;
		Class c = null;
		IVista v = null;
		s = "juego.vistas.Vista" + o.getClass().getSimpleName();
		
		Class partypes[] = new Class[2];
		Object arglist[] = new Object[2];
		
		partypes[0] = SuperficieDeDibujo.class;
		partypes[1] = ObjetoDelJuego.class;
		
		arglist[0] = controlador.getSuperficieDeDibujo();
		arglist[1] = o;		
		
		try {
			//System.out.println(s);
			c = Class.forName(s);
			v = (IVista)c.getConstructor(partypes).newInstance(arglist);
			vistasDelJuego.add(v);
		} catch (Exception e) {
			System.out.println("No hay vista creada para: " + o.getClass().getSimpleName());
		}
		return v;
	}
	
	public void agregarObjetoVivo(ObjetoDelJuego objDelJuego) {		
		if (objDelJuego instanceof AlgoTank) { 
			this.algoTank = (AlgoTank) objDelJuego;
			
		}
		if (objDelJuego instanceof Base) this.base = (Base) objDelJuego;
		
		objDelJuego.setNivel(this);

		controlador.agregarObjetoVivo(objDelJuego);

		// -----------------------------------------------------------------------------------------
		// Genero la vista para el objeto dinamicamente.
		// -----------------------------------------------------------------------------------------
		IVista v = generarVistaParaObjeto(objDelJuego);
		// -----------------------------------------------------------------------------------------
		
		controlador.agregarDibujable(v);
		
		objetosDelJuegoEnEspera.add(objDelJuego);
		objDelJuego.informarPosicion();
		/*for (int j = 0; j < Tablero.altoNivel; j ++) {
			for (int i = 0; i < Tablero.anchoNivel; i ++) {
				if (tablero.casillero(i, j) != 0) {
					System.out.print(String.valueOf(tablero.casillero(i, j)).substring(0, 4) + "	");
				} else {
					System.out.print("0	");
				}
			}
			System.out.println();
		}
		System.out.println();*/
	}
	
	public void removerObjeto(ObjetoDelJuego objDelJuego) {
		if (controlador != null) {
			IVista v = buscarVista(objDelJuego);
			controlador.removerDibujable(v);
			vistasDelJuego.remove(v);
			controlador.removerObjetoVivo(objDelJuego);			
		}		
		objetosDelJuegoBorrar.add(objDelJuego);
	}

	private IVista buscarVista(ObjetoDelJuego o) {		
		Iterator<IVista> it = vistasDelJuego.iterator();
		IVista v = null;
		IVista vista = null;
		while (it.hasNext() && vista == null) {
			v = (IVista) it.next();			
			if (v.getPosicionable().equals(o)) {
				vista = v;
			}
		}
		return vista;
	}


	public boolean sePuedeMover(ObjetoDelJuego objeto, tipoMovimiento direccion) {
		return tablero.sePuedeMover(objeto, direccion);
	}
	
	public SuperficieDeDibujo getSuperficieDeDibujo() {
		return controlador.getSuperficieDeDibujo();
	}

	public void ocuparPosicion(ObjetoDelJuego objeto) {
		tablero.ocuparPosicion(objeto);		
	}

	public void reservarPosicion(Tanque objeto) {
		tablero.reservarPosicion(objeto);		
	}
	
	
	

	@Override
	public double getX() {		
		return posicion.getX();
	}

	@Override
	public double getY() {
		return posicion.getY();
	}
	
	public void sumarPuntos(int cantidad) {
		puntos = puntos + cantidad;
	}
	
	public Base getBase() {
		return base;
	}

	public Element getElemento(Document doc) {
		Element nodoNivel = doc.createElement("Nivel");
		
		Element nodoPuntos = doc.createElement("puntos");
		nodoNivel.appendChild(nodoPuntos);
		nodoPuntos.setTextContent(String.valueOf(this.puntos));
		
		Element nodoNumero = doc.createElement("numero");
		nodoNivel.appendChild(nodoNumero);
		nodoNumero.setTextContent(String.valueOf(this.numeroNivel));
		
		Element nodoObjetos = doc.createElement("objetos");
		nodoNivel.appendChild(nodoObjetos);		
		
		Iterator<ObjetoDelJuego> it = objetosDelJuego.iterator();
		while (it.hasNext()) {
			ObjetoDelJuego o = (ObjetoDelJuego)it.next();
			Element e = o.getElemento(doc);
			
			if (e != null) nodoObjetos.appendChild(e);			
		}
		
		return nodoNivel;
	}

	public void limpiar() {		
		Iterator<ObjetoDelJuego> it = objetosDelJuego.iterator();
		ObjetoDelJuego o;
		while(it.hasNext()) {
			o = it.next();
			controlador.removerObjetoVivo(o);
			IVista v = buscarVista(o);
			controlador.removerDibujable(v);
			vistasDelJuego.remove(v);
		}
		
		objetosDelJuego.clear();
		
		
		controlador.agregarObjetoVivo(this);
				
	}

	public void setElemento(Document doc, Element element) {
		cargando = true;
		Node nPuntos, nObjetos, nNumero, n;		
		int cantidadObjetos;			
		
		nPuntos = element.getChildNodes().item(1);
		nNumero = element.getChildNodes().item(3);
		nObjetos = element.getChildNodes().item(5);
		
		cantidadObjetos = nObjetos.getChildNodes().getLength();
		
		this.numeroNivel = Integer.parseInt(nNumero.getTextContent());
		this.puntos = Integer.parseInt(nPuntos.getTextContent());
		
		String s;
		ObjetoDelJuego o;
		
		Class partypes[] = new Class[1];
		Object arglist[] = new Node[1];
		
		partypes[0] = Node.class;		
		
		for (int i = 1; i < cantidadObjetos; i = i + 2) {
			n = nObjetos.getChildNodes().item(i);
			s = n.getNodeName();
			arglist[0] = n;
			try {							
				o = (ObjetoDelJuego)Class.forName(s).getConstructor(partypes).newInstance(arglist);
				this.agregarObjetoVivo(o);
			} catch (Exception e) {
				System.out.println("Error al instanciar objeto del juego.");
				System.exit(Constantes.ERROR_INSTANCIANDO_OBJETO);
			}
		}
		cargando = false;
	}
	
	public void iniciarNivel(int numero) {
		this.getControlador().setIntervaloSimulacion(0);
		
		String metodoNivel = "iniciarNivel" + String.valueOf(numero);
		Method metodo = null;

		numeroNivel = numero;
		cantidadOleada = numeroNivel + 3;
						
		try {			
			metodo = this.getClass().getMethod(metodoNivel, (Class[])null);
		} catch (Exception ex) {
			// Fin del juego.
		}		
		
		if (metodo != null) {
			this.limpiar();					
			try {
				metodo.invoke(this, (Object[])null);
			} catch (Exception ex) {
				System.out.println("Error al iniciar el nivel: " + String.valueOf(numero));
				System.exit(Constantes.ERROR_INICIANDO_NIVEL);
			}
		} else {
			victoria();
		}
		this.getControlador().setIntervaloSimulacion(Constantes.INTERVALO_SIMULACION);
	}	

	public void iniciarNivel1() {	
		// Creo las paredes.	
		String mapa =   "0000000000000000" +
		                "2000000000000000" +
		                "2002000000000000" +
		                "2001000000000000" +
		                "2002002220000000" +
		                "2202021212020000" +
		                "1102020202020202" +
		                "0001020202020121" +
		                "0000010102220020" +
		                "0000000001110212" +
		                "0000000000000101" +
		                "0000000000000000";		
		CreadorDeParedes creadorFondo = new CreadorDeParedes();		
	    ArrayList<Pared> fondo = creadorFondo.crearParedes(mapa);	    	    
	    armarCon(fondo);	    
	}
	
	public void iniciarNivel2() {
		// Creo las paredes.	
		String mapa =   "0000000000000000" +
		                "0000000000000000" +
		                "1111111111111100" +
		                "0000000000000000" +
		                "0000000000000000" +
		                "0011111111111111" +
		                "0000000000000000" +
		                "0000000000000000" +
		                "1111111111111000" +
		                "0000000000000000" +
		                "0000000000000000" +
		                "0000000000000000";
		
		CreadorDeParedes creadorFondo = new CreadorDeParedes();
	    ArrayList<Pared> fondo = creadorFondo.crearParedes(mapa);	    

	    armarCon(fondo);
	}
	
	public void iniciarNivel3() {				
		// Creo las paredes.	
		String mapa =   "0000000000000000" +
		                "1111111001111100" +
		                "0000000000022000" +
		                "0011100220011111" +
		                "0000000220000000" +
		                "1111100220011100" +
		                "0000000220000000" +
		                "0011100220011111" +
		                "0000000000000000" +
		                "2222222002222222" +
		                "0000000000000000" +
		                "0000000000000000";
		
		CreadorDeParedes creadorFondo = new CreadorDeParedes();
	    ArrayList<Pared> fondo = creadorFondo.crearParedes(mapa);	    
	    armarCon(fondo);
	}

	

	public void gameOver() {	
		cargando = true;
		limpiar();
		controlador.removerObjetoVivo(this);
		objetosDelJuego.clear();
		Posicion posicion = new Posicion(0,0);
		GameOver gameOver = new GameOver(controlador,posicion);
		objetosDelJuego.add(gameOver);
		VistaGameOver vista = new VistaGameOver(controlador.getSuperficieDeDibujo(),gameOver);
		controlador.agregarDibujable(vista);
		vistasDelJuego.add(vista);
	}
	
	public void victoria() {	
		cargando = true;
		limpiar();
		controlador.removerObjetoVivo(this);
		objetosDelJuego.clear();
		Posicion posicion = new Posicion(0,0);
		Victoria victoria = new Victoria(controlador,posicion);
		objetosDelJuego.add(victoria);	
		VistaVictoria vista = new VistaVictoria(controlador.getSuperficieDeDibujo(),victoria);
		controlador.agregarDibujable(vista);
		vistasDelJuego.add(vista);
		
	}



	public void setObservadorPuntos(ObservadorPuntos observador) {
		observadorPuntos = observador;
		
	}
}
